package com.tfnswebdesign.common {
  
  /************************************
    Class ServerProxy
      - Wraps all database calls
        - isolated into class to futureproof logic
  ************************************/
  
  import flash.net.*;
  import flash.events.*;
  
  public class ServerProxy extends EventDispatcher {
    public static const SERVER_STATUS_DISCONNECTED:String  = "Server Status Disconnected";
    public static const SERVER_STATUS_CONNECTED:String  = "Server Status Connected";
    public static const SERVER_STATUS_ERROR:String  = "Server Status Error";
    public static const SERVER_JOB_IN_PROGRESS:String = "Server Job in Progress";
    
    private var myService:NetConnection;
    private var myServerStatus:String;
    private var theResponder:Responder;
    private var _responseCall:Function;
    private var urlLoader:URLLoader;
    private var prevCall:String;
    private var prevArgs:Array;
    private var processing:Boolean;
    
    private var myAuth:iServiceAuth;
    private var myCred:ServiceCred; 
    
    private var baseURL:String;
    
    private var jobQueue:Array;
		
		/*-- constructor --*/
		
		public function ServerProxy(outsideServiceURL:String = "") {
		  // set initial variables
		  myServerStatus = SERVER_STATUS_DISCONNECTED;
		  jobQueue = new Array();
		  processing = false;
		  
		  myAuth = null;
		
		  myService = new NetConnection();
		  
		  if (outsideServiceURL != "") {
		    connect(outsideServiceURL);
		  }
    }
    
    public function set authentication(newAuthentication:iServiceAuth) {
      myAuth = newAuthentication;
    }
    
    public function set credentials(newCredentials:ServiceCred) {
      var oldCreds = myCred.getCredentials();
      var newCreds = newCredentials.getCredentials();
      if (oldCreds['userName'] != newCreds['userName']) {
        myCred = newCredentials;
        addJob('user.logout', logOutUserPost);
        addJob('user.login', logInUserPost, newCreds['userName'], ['password']);
      }
    }
    
    protected function logOutUserPost(returnData:*) {
      myAuth.register(returnData);
    }
    
    protected function logInUserPost(returnData:*) {
      myAuth.register(returnData);
    }
    
    /*-- connect - initial connection to service --*/
    
    public function connect(myServiceURL) {
      trace("SERVER PROXY - CONNECTED");
      myService.objectEncoding = ObjectEncoding.AMF0;
      myService.connect(myServiceURL);
      theResponder = new Responder(init2, onFault);
      myService.call('system.connect', theResponder);
    }
    
    protected function init2(returnObject:*) {
      // connected, time to login
      if (myAuth) {
        myAuth.register(returnObject);
      }
      
      myServerStatus = SERVER_STATUS_CONNECTED;
      
      // check jobqueue for jobs
      if (jobQueue.length > 0) {
        var job:Object = jobQueue.shift();
        _call(job.serviceCall, job.returnCall, job.args);
      }
      
      dispatchEvent(new Event(Event.COMPLETE));
      
    }
        
    /************************************
      private functions
    ************************************/
    
    /*-- addJob - actual call for service, handles job queue --*/
    
    public function addJob(serviceCall:String, returnCall:Function, ...args) {   
      if (getServerStatus() == SERVER_STATUS_CONNECTED && !processing) {
        // call service
        _call(serviceCall, returnCall, args);
      } else {
        // still cool, just need to add job to queue
        var job:Object = new Object();
        job.serviceCall = serviceCall;
        job.returnCall = returnCall;
        job.args = args;
        jobQueue.push(job);
        trace("SERVICE PROXY - job added " + serviceCall + " " + args[0] + " queuelength: " + jobQueue.length);
      }
    }
    
    /*-- _call - calls service functions --*/
    
    protected function _call(serviceCall:String, returnCall:Function, ...args) {
      myServerStatus = SERVER_JOB_IN_PROGRESS;
      prevCall = serviceCall;
      prevArgs = args[0];
      theResponder = new Responder(_callResponse, onFault);
      _responseCall = returnCall;
      processing = true;
      
      // since we don't call this function except from addJob, we need to pull args out of it's own array
      args = args[0];
      
      trace("");
      trace("START***************");
      trace("SERVER PROXY - return function call " + " " + serviceCall + " " + returnCall);
      for (var i:int = 0; i < args.length; i++) {
        if (typeof(args[i]) == 'object' && args[i].length) {
          for (var j:int = 0; j < args[i].length; j++) {
            trace("SERVER PROXY - args " + i + " " + j + " " + typeof(args[i][j]) + ": " + args[i][j]);
          }
        } else {
          trace("SERVER PROXY - args " + i + " " + typeof(args[i]) + ": " + args[i]);
        }
      }     
      
      trace("***************");
      trace("");
      
      // concats arguments into array, calls service
      var serviceCallArgs:Array = new Array(serviceCall, theResponder);
      if (myAuth != null) {
        serviceCallArgs = myAuth.authenticate(serviceCallArgs);
      }
      serviceCallArgs = serviceCallArgs.concat(args);
      myService.call.apply(this, serviceCallArgs);      
    }
    
    protected function _callResponse(returnObject:*) {
      if (jobQueue.length <= 0) {
        myServerStatus = SERVER_STATUS_CONNECTED;
      }
      
      /*trace("");
      trace("END***************");
      trace("SERVER PROXY POST RESPONSE " + prevCall);
      trace("SERVER PROXY POST - return function call " + _responseCall + " job queue length: " + jobQueue.length);
      for (var i:int = 0; i < prevArgs.length; i++) {
        if (typeof(prevArgs[i]) == 'object' && prevArgs[i].length) {
          for (var j:int = 0; j < prevArgs[i].length; j++) {
            trace("SERVER PROXY POST - args " + i + " " + j + " " + typeof(prevArgs[i][j]) + ": " + prevArgs[i][j]);
          }
        } else {
          trace("SERVER PROXY POST - args " + i + " " + typeof(prevArgs[i]) + ": " + prevArgs[i]);
        }
      }
      trace("SERVER PROXY POST - data " + prevCall + " " + typeof(returnObject));
      for (var key in returnObject) {
        trace("SERVER PROXY POST - return Object: " + key + " " + returnObject[key]);
      }
      
      trace("***************");
      trace("");*/
      
      try {
        if (_responseCall != null) {
          _responseCall(returnObject);
        }
      } catch(e) {
        //trace("SOMETHING FUNKY IS GOING ON HERE " + prevCall);
      } finally {
        processing = false;
        
        if (jobQueue.length > 0) {
          var job:Object = jobQueue.shift();
          //trace("SERVER PROXY - NEW JOB FROM QUEUE " + job.serviceCall + job.args[0] + " " + myServerStatus);
          _call(job.serviceCall, job.returnCall, job.args);
        } else {
          dispatchEvent(new Event(SERVER_STATUS_CONNECTED));
        }      
      }
    }
    
    protected function onFault(f:*) {
      myServerStatus = SERVER_STATUS_ERROR;
      trace("Error" + f);
      for (var item:* in f) {
        trace(item + ": " + f[item]);
      }
      dispatchEvent(new Event(SERVER_STATUS_ERROR));
    }
    
    public function getServerStatus() {
      return myServerStatus;
    }
    
    public function getBaseURL() {
      return baseURL;
    }
  }	
}